(ns clj.sildiceprob
  (:use [clojure.contrib.probabilities.finite-distributions :only (binomial)])
  (:use [clojure.contrib.math :only (expt)]))

(defn- eventProbability [i result dices sides]
  (* (/ 1 sides) 
    (expt (/ (- result 1) sides) i ) 
    (expt (/ result sides) (- dices 1 i))
    ))

(defn- probabilityFor [result dices sides ]
	(let [x (map eventProbability (range 0 dices) (repeat result) (repeat dices) (repeat sides))] 	
	(reduce + x))
	)

(defn maximalRoll [dices sides]
  {:pre [(> dices 0) (> sides 1)] }
  "(maximalRoll dices sides) returns the maximal possible roll with the given dices"
  (+ sides (- dices 1))
  )
	
(defn rollHighestValue [times dices sides]
	{:pre [(<= times dices) (> times 0)]}
  "(rollHighestValue times dices sides) calculates the probability to roll exactly t times the highest possible value with the given number of dices with the given number of sides" 
	((binomial dices (/ 1 sides)) times)
	)  

(defn rollFumble [dices sides]
  "(rollFumble dices sides) calculates the possibility to roll a fumble with the given number of dices with the specified numbe of sides" 
  (expt (/ 1 sides) dices))

(defn probToRoll 
  "(probToRoll result dices sides) calculates the probability to roll the expected result with one dice with the gievn number of sides.
(probToRoll result sides dices) calculates the probability to roll the expected result with a given number of dices with the given number of sides ."
  ( [result sides]
	{:pre [(> result 0)]}
  (if (<= result sides)(/ 1 sides) 0)
  )
  
  ([result dices sides]
	{:pre [(> result -1)]}
    (cond
      (= result 0) (rollFumble dices sides )
      (= result 1) 0
      (< result sides) (probabilityFor result dices sides)
      :else (rollHighestValue (+ 1 (- result sides)) dices sides)
      )))

(defn rangeProbability [i limit dices sides]
  {:post [(>= 1 (reduce + %)) (>= (reduce + %) 0)]}
  "(rangeProbability i limit dices sides) returns a lazy-sequence with the probabilities from i to limit."
           (lazy-seq (
                       when (< i (+ 1 limit))
                       (cons (probToRoll i dices sides) (rangeProbability (inc i) limit dices sides)))))

(defn probabilityMap 
  "(probabilityMap dices side) returns a map which contains the probabilites for all possible rolls with the given dices."
  ([dices sides]
    (zipmap (range 0 (+ 1 (maximalRoll dices sides)))(rangeProbability 0 (maximalRoll dices sides) dices sides))
    )
  )